/**
 * 
 */
package org.dragonfire.dasm.directives;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;

import org.dragonfire.dasm.Command;
import org.dragonfire.dasm.CompileException;
import org.dragonfire.dasm.DASM;
import org.dragonfire.dasm.Listing;
import org.dragonfire.dasm.DirectiveManager.Directive;
import org.dragonfire.file.BufferedFile;

import com.stevesoft.pat.Regex;

/**
 * Internal Directive used to manage file reading/writing<br />
 * <br />
 * Format:<br />
 * <code>#FILE OPEN|CLOSE|RESET|SKIP,<i>&lt;handle&gt;</i>[,<i>&lt;fileName&gt;</i>|<i>&lt;numBytes&gt;</i>]</code><br />
 * <br />
 * Parameters:<br />
 * <table>
 * <tr>
 * <td><code>command</code></td>
 * <td>The operation to perform:<table>
 * <tr>
 * <td><code>OPEN</code></td>
 * <td>Opens a new file and associates it with <code>handle</code></td>
 * </tr>
 * <tr>
 * <td><code>CLOSE</code></td>
 * <td>Closes the file associated with <code>handle</code></td>
 * </tr>
 * <tr>
 * <td><code>RESET</code></td>
 * <td>Resets the pointer associated with <code>handle</code> to the begining
 * of the file, position 0</td>
 * </tr>
 * <tr>
 * <td><code>SKIP</code></td>
 * <td>Increments the pointer associated with <code>handle</code>, skipping
 * over <code>numBytes</code> bytes.</td>
 * </tr>
 * </table></td>
 * </tr>
 * <tr>
 * <td><code>handle</code></td>
 * <td>The file handle to operate on. Valid handles may contain the same
 * characters as a valid label (see {@link org.dragonfire.dasm.DASM#LABEL_REGEX}
 * for a regular expression that matches valid lables)</td>
 * </tr>
 * <tr>
 * <td><code>fileName</code></td>
 * <td>The filename to open, only applies to the <code>OPEN</code> command.</td>
 * </tr>
 * <tr>
 * <td><code>numBytes</code></td>
 * <td>The number of bytes to skip over. Only applies to the <code>SKIP</code>
 * command.</td>
 * </tr>
 * </table><br />
 * Errors:<br />
 * <table>
 * <tr>
 * <td><code>ERROR_NOT_ENOUGH_ARGS</td><td>Thrown if any of the arguments are missing.</td></tr>
 * <tr><td><code>ERROR_STRING_REQUIRED</td><td>Thrown if <code>handle</code>, <code>command</code>, or <code>fileName</code> are not strings</td></tr>
 * <tr><td><code>ERROR_INT_REQUIRED</td><td>Thrown if <code>numBytes</code> is not an integer</td></tr>
 * <tr><td><code>ERROR_FILE_OPERATION</code></td><td>Thrown if there is an error trying to reset the file pointer</td></tr>
 * <tr><td><code>ERROR_BAD_MODULE</code></td><td>Thrown if <code>handle</code> contains invalid characters.</td></tr>
 * </table>
 * @author Darth Android
 * @version 2.0
 * @see java.io.DataInputStream
 */
public class File extends Directive {

	/**
	 * Map of handles to files
	 */
	private HashMap<String, InputStream> files = new HashMap<String, InputStream>();

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ArrayList<Listing> pass1(Command c) {
		ArrayList<Listing> list = new ArrayList<Listing>();
		String cmd = c.getParam(0).toLowerCase();
		String handle = evalString(c.getParam(1)).toLowerCase();
		if (!Regex.matches(DASM.LABEL_REGEX, handle)) {
			throw new CompileException("ERROR_BAD_HANDLE", handle);
		}
		if (cmd.equals("open")) {
			String name = evalString(c.getParam(2));
			files.put(handle, BufferedFile.openFile(name));
		}
		else if (cmd.equals("reset")) {
			if (!files.containsKey(handle))
				throw new CompileException("WARN_FILE_UNKNOWN_HANDLE", handle);
			try {
				files.get(handle).reset();
			} catch (IOException ioe) {
				throw new CompileException("ERROR_FILE_OPERATION", handle);
			}
		}
		else if (cmd.equals("close")) {
			if (!files.containsKey(handle))
				throw new CompileException("WARN_FILE_UNKNOWN_HANDLE", handle);
			files.remove(handle);
		}
		else if (cmd.equals("skip")) {
			int bytes = evalInt(c.getParam(2));
			if (!files.containsKey(handle))
				throw new CompileException("WARN_FILE_UNKNOWN_HANDLE", handle);
			try {
				int skippedBytes = 0;
				files.get(handle.toLowerCase()).skip(bytes);
				if (bytes != skippedBytes)
					warn("WARN_FILE_SKIP_PARTIAL", bytes, handle, skippedBytes);
			} catch (IOException ioe) {
				warn("WARN_FILE_SKIP_FAILED", bytes, handle, ioe.getMessage());
			}
		}
		return list;
	}
}
